﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace BaseDomainNamespace.Viewpoints
{
    /// <summary>
    /// 複数の順序で構成される順序を表します。
    /// </summary>
    /// <typeparam name="TEntity">エンティティの型。</typeparam>
    /// <seealso cref="Order&lt;TEntity&gt;"/>
    [Serializable]
    public sealed class ComplexOrder<TEntity> : Order<TEntity>
        where TEntity : class
    {
        /// <summary>
        /// <see cref="ComplexOrder&lt;TEntity&gt;"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="first">基になる順序。</param>
        /// <param name="followings">後続の順序。</param>
        /// <exception cref="System.ArgumentNullException">first が null です。</exception>
        internal ComplexOrder(Order<TEntity> first, params Order<TEntity>[] followings)
        {
            if (first == null)
            {
                throw new ArgumentNullException("first");
            }
            _first = first;
            _followings = (followings ?? new Order<TEntity>[0]).ToList().AsReadOnly();
        }


        /// <summary>
        /// 基になる順序。
        /// </summary>
        private readonly Order<TEntity> _first;

        /// <summary>
        /// 後続の順序。
        /// </summary>
        private readonly ReadOnlyCollection<Order<TEntity>> _followings;


        /// <summary>
        /// 基になる順序を取得します。
        /// </summary>
        public Order<TEntity> First
        {
            get
            {
                return _first;
            }
        }

        /// <summary>
        /// 後続の順序を取得します。
        /// </summary>
        public ReadOnlyCollection<Order<TEntity>> Followings
        {
            get
            {
                return _followings;
            }
        }


        /// <inheritdoc/>
        internal override IOrderedQueryable<TEntity> OrderFirstly(IQueryable<TEntity> target)
        {
            var result = _first.OrderFirstly(target);

            foreach (var following in _followings)
            {
                result = following.OrderFurthermore(result);
            }

            return result;
        }

        /// <inheritdoc/>
        internal override IOrderedQueryable<TEntity> OrderFurthermore(IOrderedQueryable<TEntity> target)
        {
            var result = _first.OrderFurthermore(target);

            foreach (var following in _followings)
            {
                result = following.OrderFurthermore(result);
            }

            return result;
        }


        /// <summary>
        /// 指定した順序を後続の順序として組み合わせた、新しい順序を生成します。
        /// </summary>
        /// <param name="following">後続の順序。</param>
        /// <returns>指定した順序を後続の順序として組み合わせた、新しい順序。</returns>
        /// <exception cref="System.ArgumentNullException">following が null です。</exception>
        public override ComplexOrder<TEntity> Then<TFollowingValue>(SimpleOrder<TEntity, TFollowingValue> following)
        {
            if (following == null)
            {
                throw new ArgumentNullException("following");
            }
            var followings = Enumerable.Concat(_followings, new[] { following }).ToArray();
            return new ComplexOrder<TEntity>(_first, followings);
        }
    }
}
